package hidra.core.population;

import java.io.BufferedWriter;
import java.io.IOException;

import hidra.core.util.Util;
import hidra.experiments.GlobalSettings;
import hidra.jmetal.core.Algorithm;
import hidra.jmetal.core.Problem;
import hidra.jmetal.core.SolutionSet;
import hidra.qualityIndicator.QualityIndicator;
import jmetal.util.JMException;

public abstract class HIDRAPopulationAlgorithm extends Algorithm{

	protected int iteration_;
	protected int maxIterations_;	
	protected QualityIndicator qualityIndicator;
	protected double globalTime_ ;	
	
	//parameters
	protected double generationalDistance;
	protected double invertedGenerationalDistance;
	double improvedMaximimSpread;
	double spacing;
	double hypervolume;
	double epsilon;
	double time;	

	protected boolean stopCondition = false;

	protected void initParams(){
		this.iteration_ = 0;		
		this.globalTime_ = 0.0;
		this.maxIterations_ = ((Integer) getInputParameter("maxIterations")).intValue();
		this.stopCondition = false;
		this.generationalDistance = 0;
		this.invertedGenerationalDistance = 0;
		this.improvedMaximimSpread = 0;
		this.spacing = 0;
		this.hypervolume = 0;
		this.epsilon = 0;
		this.time = 0;
	}

	public HIDRAPopulationAlgorithm(Problem problem) {
		super(problem);
	}

	public void printMetrics(String path){
		Util.printData(globalTime_, path + "/T");
		Util.printData(hypervolume, path + "/HV");
		Util.printData(invertedGenerationalDistance, path + "/IGD");
		Util.printData(generationalDistance, path + "/GD");
		Util.printData(spacing, path + "/S");
		Util.printData(improvedMaximimSpread, path + "/MS");
		Util.printData(epsilon, path + "/E");
	}
	
	private void calculateMetrics(SolutionSet solutionSet) {
		hypervolume = qualityIndicator.getHypervolume(solutionSet);
		invertedGenerationalDistance = qualityIndicator.getIGD(solutionSet);
		generationalDistance = qualityIndicator.getGD(solutionSet);
		spacing = qualityIndicator.getSpacing(solutionSet);
		improvedMaximimSpread = qualityIndicator.getImprovedMaximumSpread(solutionSet);
		epsilon = qualityIndicator.getEpsilon(solutionSet);
	}

	@Override
	public SolutionSet execute() throws JMException, ClassNotFoundException {

		qualityIndicator = (QualityIndicator) getInputParameter("indicators");

		String tmpPath = (String) getInputParameter("pathParetoFront");
		String pathDiretoryFinal = (String) getInputParameter("pathDiretoryFinal");

		long t1 = System.currentTimeMillis();
		SolutionSet	solutionSet = initializationAlgorithm();
		long t2 = System.currentTimeMillis();

		time = (t2 - t1);	
		globalTime_ += time;

		while ( !stopCondition && (iteration_ < maxIterations_) ){

			t1 = System.currentTimeMillis();
			solutionSet = runIteration();			
			t2 = System.currentTimeMillis();

			time = (t2 - t1);
			globalTime_ += time;				

			calculateMetrics(solutionSet);

			if (GlobalSettings.CALCULATE_METRICS) {	 
				printMetrics(tmpPath);
			}

		}

		if(GlobalSettings.PRINT_PARETO){

			String pathParetoFront = tmpPath + "/IFUN_" + iteration_ ;				
			String pathParetoSet = tmpPath + "/IVAR_" + iteration_;

			solutionSet.printObjectivesToFile(pathParetoFront);
			solutionSet.printVariablesToFile(pathParetoSet);

		}

		SolutionSet finalSolutionSet = this.getParetoFront();
		calculateMetrics(finalSolutionSet);

		if (GlobalSettings.CALCULATE_METRICS){				
			printMetrics(pathDiretoryFinal); 
		}

		return finalSolutionSet;
	}

	protected abstract SolutionSet initializationAlgorithm() throws ClassNotFoundException,
	JMException;
	protected abstract SolutionSet runIteration() throws JMException;
	protected abstract SolutionSet getParetoFront();

}
